package com.sctek.smartglasses.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.net.HttpURLConnection;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;

import android.content.Context;
import android.content.ContentValues;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.provider.MediaStore.Images.ImageColumns;
import android.provider.MediaStore.Video.VideoColumns;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import com.sctek.smartglasses.utils.CustomHttpClient;
import com.sctek.smartglasses.utils.MediaData;
import com.sctek.smartglasses.ui.MainActivity;

public class RemoteMediaDownloader {
	
	public static final String TAG = "RemoteMediaDownloader";
	public static final boolean DEBUG = true;

	public static final String PHOTO_DOWNLOAD_FOLDER = 
		Environment.getExternalStorageDirectory().toString() + "/SmartGlasses/photos";
	public static final String VIDEO_DOWNLOAD_FOLDER = 
		Environment.getExternalStorageDirectory().toString() + "/SmartGlasses/videos";
	private static RemoteMediaDownloader sInstance = null;
	private Context mContext;	
	private ArrayList<MediaData> mRemoteMediaList;
	private boolean mIsDownloading = false;

	public static RemoteMediaDownloader getInstance(Context context) {
		if(null == sInstance)
			sInstance = new RemoteMediaDownloader(context);
		return sInstance;
	}

	private RemoteMediaDownloader(Context context) {
		mContext = context;
	}

	class GetRemoteMediaUrlTask extends AsyncTask<String, Integer, String> {
		private final static String TAG = "GetRemoteMediaUrlTask";
		public GetRemoteMediaUrlTask() {
		}
		
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
		}

		@Override
		protected void onPostExecute(String result) {
			if(DEBUG) Log.i(TAG,"onPostExecute result:"+result);
			executeDownloadRemoteMediaTask(result);
			super.onPostExecute(result);
		}

		@Override
		protected String doInBackground(String... type) {
			getMediaUrl(type[0], type[1]);
			return type[1];
		}
		
		@Override
		protected void onProgressUpdate(Integer ...values) {
			super.onProgressUpdate(values);
		}
	}
	
	private boolean getMediaUrl(final String ip, final String type) {
		String uri = String.format("http://" + ip + "/cgi-bin/listfiles?%s", type);
		if(DEBUG) Log.i(TAG,"uri: "+uri);
		final HttpClient httpClient = CustomHttpClient.getHttpClient();
		final HttpGet httpGet = new HttpGet(uri);
		boolean ok = false;
		ok = httpRequestExecute(httpClient, httpGet);
		if(ok) {
			mRemoteMediaList = getMediaData(ip);
		}
		if(mRemoteMediaList == null || !ok) {
			return false;
		}
		//Adjust the document order, in descending order by name.
		Comparator<MediaData> comparator = new Comparator<MediaData>(){  
			public int compare(MediaData s1, MediaData s2) {    
				return s1.name.compareTo(s2.name);  
			}  
		};
		Collections.sort(mRemoteMediaList,comparator);
		Collections.reverse(mRemoteMediaList);
		return true;
	}

	private ArrayList<MediaData> getMediaData(String ip) {
	        File xmlFile = new File(mContext.getCacheDir(), "medianame.xml");
	        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	        try {
                        DocumentBuilder builder = factory.newDocumentBuilder();
                        Document dom = builder.parse(new FileInputStream(xmlFile));
                        Element root = dom.getDocumentElement();
                        String urlPrefix = "";
                        if ("photos".equals(root.getNodeName())) {
                                urlPrefix = "http://" + ip + "/data/GlassData/photos/";
                        } else {
                                urlPrefix = "http://" + ip + "/data/GlassData/videos/";
                        }
                        NodeList items = root.getElementsByTagName("name");
                        ArrayList<MediaData> mediaList = new ArrayList<MediaData>();
                        for (int i = 0, j = items.getLength(); i < j; i++) {
                                Element node = (Element) items.item(i);
                                if (Node.ELEMENT_NODE == node.getNodeType()) {
                                        MediaData md = new MediaData();
                                        String name = node.getFirstChild().getNodeValue();
                                        Log.i(TAG, name);
                                        md.setName(name);
                                        md.setUrl(urlPrefix + name); 
                                        mediaList.add(md);
                                }
                        }
                        return mediaList;
                } catch (Exception e) {
                        e.printStackTrace();
                }
	        return null;
	}
	
	private boolean httpRequestExecute(HttpClient httpclient, HttpGet httpget) {
		InputStream in = null;
		int retry = 3;
		while (retry-- != 0) {
			try{
				HttpResponse response = httpclient.execute(httpget);
				in = response.getEntity().getContent();
				
				byte[] buffer = new byte[4096];
				
				File cacheDir = mContext.getCacheDir();
				File xmlFile = new File(cacheDir, "medianame.xml");
				FileOutputStream fo = new FileOutputStream(xmlFile, false);

				boolean isFirst = true;
				int n = 0;
				while((n = in.read(buffer)) != -1){
					// Remove blank characters in the header.
					int i = 0;
					if (isFirst) {
						isFirst = false;
						for (; i < n; i++) {
							if ((buffer[i] & 0xFF) == '<') {
								break;
							}
						}
					}
					fo.write(buffer, i, n-i);
				}
				
				fo.close();
				in.close();
				
				return true;	
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		return false;
	}

	public void startDownloadRemoteMedia(String glassIp, String type) {
		if(DEBUG) Log.i(TAG,"mIsDownloading = "+mIsDownloading);
		if(!mIsDownloading) {
			mIsDownloading = true;
			new GetRemoteMediaUrlTask().execute(new String[]{glassIp, type});
		}
	}

	public void executeDownloadRemoteMediaTask(String type) {
		new DownloadRemoteMediaTask().execute(new String[]{type});
	}

	class DownloadRemoteMediaTask extends AsyncTask<String, Integer, String> {
		private final static String TAG = "DownloadRemoteMediaTask";
		public DownloadRemoteMediaTask() {
		}
		
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
		}

		@Override
		protected void onPostExecute(String result) {
			if(DEBUG) Log.i(TAG,"onPostExecute.");
			super.onPostExecute(result);
		}

		@Override
		protected String doInBackground(String... type) {
			downloadMediaFile(type[0]);
			return "";
		}
		
		@Override
		protected void onProgressUpdate(Integer ...values) {
			super.onProgressUpdate(values);
		}
	}

	private void downloadMediaFile(String type) {
		String downloadFolder = VIDEO_DOWNLOAD_FOLDER;
		if(type.equals("photos")){
			downloadFolder = PHOTO_DOWNLOAD_FOLDER;
		}
		while(mRemoteMediaList.size() != 0) {
			if(DEBUG) Log.i(TAG, "downloadMediaFile size = " + mRemoteMediaList.size());
			MediaData data = mRemoteMediaList.get(0);
			GlassImageDownloader downloader =  new GlassImageDownloader();
			long startPostion = 0;
			long videoLeng = 0;
			HttpURLConnection conn = null;
			File file = new File(VIDEO_DOWNLOAD_FOLDER, data.name);
			conn = downloader.createConnection(data.url, 0);
			videoLeng = conn.getContentLength();
			if(file.exists()) {
				if(file.length() < videoLeng) {
					startPostion = file.length();
				}
				else {
					conn.disconnect();
					//delete remote file
					deleteRemoteMediaFile(type, data);
					continue;
				}
			}
			conn.disconnect();
			
			File dir = new File(VIDEO_DOWNLOAD_FOLDER);
			if(!dir.exists())
				dir.mkdirs();
			
			try {
				InputStream in = downloader.getInputStream(data.url, startPostion);
				
				FileOutputStream os = new FileOutputStream(file, true);
				byte[] buffer = new byte[4096];
				int len = 0;
				long downLoadedLength = startPostion;
				long tempDownLoadLength = 0;
				long delayTime = 0;
				long notifyTime = System.currentTimeMillis();
				while((len = in.read(buffer)) != -1) {
					if(DEBUG) Log.i(TAG, "" + len);
					downLoadedLength += len;
					tempDownLoadLength += len;
					os.write(buffer, 0, len);
					delayTime = System.currentTimeMillis() - notifyTime;
					if(delayTime > 2000) {
						notifyTime = System.currentTimeMillis();
						tempDownLoadLength = 0;
					}
				}
				os.close();
				in.close();
				String filePath = downloadFolder + "/" + data.name;
				notifyMediaScanner(filePath);
				deleteRemoteMediaFile(type, data);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		mIsDownloading = false;
	}

	private void deleteRemoteMediaFile(String type, MediaData data) {
		int errors = 0;
		int dones = 0;
		String temp = data.url.replace("//", "");
		String glassIp = temp.substring(temp.indexOf(":")+1, temp.indexOf("/"));
		String urlPref = String.format("http://%s/cgi-bin/deletefiles?", glassIp);
		StringBuffer urlBuffer = new StringBuffer(1024);
		urlBuffer.append(urlPref);
		urlBuffer.append(type);
		urlBuffer.append("&" + data.name);
		try {
			HttpClient httpClient = CustomHttpClient.getHttpClient();
			HttpGet httpGet = new HttpGet(urlBuffer.toString());
			int delcount = GlassImageDownloader.deleteRequestExecute(httpClient, httpGet);
				
			if(delcount < 0) {
				errors++;
			}else {
				dones++;
			}
				
		} catch (Exception e) {
			e.printStackTrace();
		}
		if(errors == 0) {
			int i = mRemoteMediaList.indexOf(data);
			if(i != -1)
				mRemoteMediaList.remove(i);
		}
		Log.i(TAG, "errors:" + errors + " dones:" + dones);
	}

	private void notifyMediaScanner(String filePath) {
		ContentValues values = new ContentValues(2);
		Uri uri = null;
		if (filePath.endsWith(".mp4")) {
			values.put(VideoColumns.MIME_TYPE, "video/mp4");
			values.put(VideoColumns.DATA, filePath);
			uri = mContext.getContentResolver().insert(Video.Media.EXTERNAL_CONTENT_URI, values);
		} else if (filePath.endsWith(".3gp")) {
			values.put(VideoColumns.MIME_TYPE, "video/3gpp");
			values.put(VideoColumns.DATA, filePath);
			uri = mContext.getContentResolver().insert(Video.Media.EXTERNAL_CONTENT_URI, values);
		} else {
			values.put(ImageColumns.MIME_TYPE, "image/jpeg");
			values.put(ImageColumns.DATA, filePath);
			uri = mContext.getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values);
		}
	}

	public void setDownloadState(boolean downloading) {
		mIsDownloading = downloading;
	}

	public boolean isDownloading() {
		return mIsDownloading;
	}
}